package miggy.ui;

import miggy.SystemModel;
import miggy.DisassembledItem;
import miggy.cpu.InstructionDecoder;
import miggy.api.cpu.MC680x0;
import miggy.api.machine.MachineCore;
import miggy.api.machine.MachineListener;
import miggy.api.machine.MachineState;
import miggy.utils.TextUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

// $Revision: 21 $
public class Console implements MachineListener
{
	private final MachineCore machine;
	private Thread consoleThread;
	private volatile boolean exit;
	private volatile MachineState state;

	public Console(MachineCore machine)
	{
		this.machine = machine;
		exit = false;
	}

	public void init()
	{
		machine.addListener(this);
		consoleThread = new Thread(new Runnable()
		{
			public void run()
			{
				runConsole();
			}
		});

		consoleThread.start();
	}

	private synchronized void runConsole()
	{
		banner();
		state = machine.getState();

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

		while(!exit)
		{
			System.out.print("> ");
			try
			{
				while(!exit && !in.ready())
				{
					//have to do polling because we cannot interrupt a read on System.in
					//unless we pull the rug out and do System.exit(0) somewhere
					//So we sleep and only check every half second for input.
					wait(500);
				}

				if(!exit)
				{
					//should not block as we've checked that there is data ready for us
					String cmd = in.readLine();

					if(cmd != null && cmd.length() > 0)
					{
						parseCmd(cmd);
					}
				}
			}
			catch(InterruptedException e)
			{
				System.out.println("Bye.");
			}
			catch(IOException e)
			{
				e.printStackTrace();
			}
		}
	}

	private void banner()
	{
		System.out.println("Miggy - Java Amiga Emulator - Copyright (C) 2008, Tony Headford");
		System.out.println("Interactive Console");
		System.out.println("Enter '?' for help\n");
	}

	private void parseCmd(String cmd)
	{
		switch(cmd.charAt(0))
		{
			case 'r':
			{
				if(state == MachineState.Stopped)
					showRegs();
				else
					System.out.println("Machine must be stopped first.");
				break;
			}
			case 'g':
			{
				//go - run the emulator
				if(state == MachineState.Stopped)
					machine.start();
				else
					System.out.println("Machine is already running.");

				break;
			}
			case 's':
			{
				//stop
				if(state == MachineState.Running)
					machine.stop();
				else
					System.out.println("Machine is not running.");

				break;
			}
			
			case 'q':
			{
				//quit
				System.out.println("Exiting ...");
				exit = true;
				machine.exit();
				break;
			}
		}
	}

	private void showRegs()
	{
		MC680x0 cpu = SystemModel.CPU;
		int d[] = new int[8];
		int a[] = new int[8];

		for(int n = 0; n < 8; n++)
		{
			d[n] = cpu.getDataRegister(n);
			a[n] = cpu.getAddrRegister(n);
		}

		System.out.print("d0: " + TextUtil.toHex(cpu.getDataRegister(0)));
		System.out.print("  d1: " + TextUtil.toHex(cpu.getDataRegister(1)));
		System.out.print("  d2: " + TextUtil.toHex(cpu.getDataRegister(2)));
		System.out.println("  d3: " + TextUtil.toHex(cpu.getDataRegister(3)));

		System.out.print("d4: " + TextUtil.toHex(cpu.getDataRegister(4)));
		System.out.print("  d5: " + TextUtil.toHex(cpu.getDataRegister(5)));
		System.out.print("  d6: " + TextUtil.toHex(cpu.getDataRegister(6)));
		System.out.println("  d7: " + TextUtil.toHex(cpu.getDataRegister(7)));

		System.out.print("a0: " + TextUtil.toHex(cpu.getAddrRegister(0)));
		System.out.print("  a1: " + TextUtil.toHex(cpu.getAddrRegister(1)));
		System.out.print("  a2: " + TextUtil.toHex(cpu.getAddrRegister(2)));
		System.out.println("  a3: " + TextUtil.toHex(cpu.getAddrRegister(3)));

		System.out.print("a4: " + TextUtil.toHex(cpu.getAddrRegister(4)));
		System.out.print("  a5: " + TextUtil.toHex(cpu.getAddrRegister(5)));
		System.out.print("  a6: " + TextUtil.toHex(cpu.getAddrRegister(6)));
		System.out.println("  a7: " + TextUtil.toHex(cpu.getAddrRegister(7)));

		System.out.print("sr: " + TextUtil.toHex(cpu.getSR()) + "  " + TextUtil.getSRFlags());
		System.out.print("           ss: " + TextUtil.toHex(cpu.getSSP()));
		System.out.println("  us: " + TextUtil.toHex(cpu.getUSP()));

		DisassembledItem item = DisassembledItem.makeDisassembledItem(InstructionDecoder.decode(SystemModel.CPU.getPC()));
		System.out.println("pc: " + TextUtil.toHex(cpu.getPC()) + "    " + item.getInstruction());

	}

	public void stateChange(MachineState state)
	{
		this.state = state;
	}

	public synchronized void exit()
	{
		//we might've shutdown the machine so only do this if it wasn't initiated by us
		if(!exit)
		{
			//the machine is exiting ...
			System.out.println("Exiting...");
			exit = true;
			consoleThread.interrupt();
		}
	}
}
